package com.skyd.core.game;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.Hashtable;

import com.skyd.core.common.Point;
import com.skyd.core.vector.Vector2DF;

/**
 * 寻路者
 * 
 * @author SkyD
 * 
 */
public class GamePathfinder {

	public GamePathfinder(IGameMapPiece[][] data) {
		_MapData = data;
	}

	/**
	 * 初始值为null。 功能注释：地图数据
	 */
	private IGameMapPiece[][] _MapData = null;

	/**
	 * 获取MapData。 功能注释：地图数据
	 * 
	 * @return 当前的IGameMapPiece[][]类型的值
	 */
	public IGameMapPiece[][] getMapData() {
		return _MapData;
	}

	/**
	 * 设置MapData。 功能注释：地图数据
	 * 
	 * @param value
	 *            要设置的IGameMapPiece[][]类型值
	 */
	public void setMapData(IGameMapPiece[][] value) {
		_MapData = value;
	}

	/**
	 * 设置MapData为其默认值(null)。 功能注释：地图数据
	 */
	public void setMapDataToDefault() {
		setMapData(null);
	}

	/**
	 * 初始值为null。 功能注释：起点
	 */
	private Point _StartingPoint = null;

	/**
	 * 获取StartingPoint。 功能注释：起点
	 * 
	 * @return 当前的Point类型的值
	 */
	public Point getStartingPoint() {
		return _StartingPoint;
	}

	/**
	 * 设置StartingPoint。 功能注释：起点
	 * 
	 * @param value
	 *            要设置的Point类型值
	 */
	public void setStartingPoint(Point value) {
		_StartingPoint = value;
	}

	/**
	 * 设置StartingPoint为其默认值(null)。 功能注释：起点
	 */
	public void setStartingPointToDefault() {
		setStartingPoint(null);
	}

	/**
	 * 初始值为null。 功能注释：终点
	 */
	private Point _FinishingPoint = null;

	/**
	 * 获取FinishingPoint。 功能注释：终点
	 * 
	 * @return 当前的Point类型的值
	 */
	public Point getFinishingPoint() {
		return _FinishingPoint;
	}

	/**
	 * 设置FinishingPoint。 功能注释：终点
	 * 
	 * @param value
	 *            要设置的Point类型值
	 */
	public void setFinishingPoint(Point value) {
		_FinishingPoint = value;
	}

	/**
	 * 设置FinishingPoint为其默认值(null)。 功能注释：终点
	 */
	public void setFinishingPointToDefault() {
		setFinishingPoint(null);
	}

	/**
	 * 查找周围地图块
	 * 
	 * @param point
	 *            位置
	 * @param allowInclined
	 *            允许斜向移动
	 * @param ignoreHinder
	 *            忽略障碍
	 * @return 周围地图块
	 */
	protected CopyOnWriteArrayList<IGameMapPiece> CheckAround(Point point,
			boolean allowInclined, boolean ignoreHinder) {
		IGameMapPiece[][] map = getMapData();
		int mx = map.length;
		int my = map[0].length;

		// if(point.x-1>=0){
		// oa.add(getMapPiece(point.x-1,point.y));
		// // if(point.y-1>=0)oa.add(getMapPiece(point.x-1,point.y-1));
		// // if(point.y+1<my)oa.add(getMapPiece(point.x-1,point.y+1));
		// }
		// if(point.x+1<mx){
		// oa.add(getMapPiece(point.x+1,point.y));
		// // if(point.y-1>=0)oa.add(getMapPiece(point.x+1,point.y-1));
		// // if(point.y+1<my)oa.add(getMapPiece(point.x+1,point.y+1));
		// }
		// if(point.y-1>=0)oa.add(getMapPiece(point.x,point.y-1));
		// if(point.y+1<my)oa.add(getMapPiece(point.x,point.y+1));
		CopyOnWriteArrayList<IGameMapPiece> oa = new CopyOnWriteArrayList<IGameMapPiece>();
		boolean l = false;
		boolean r = false;
		boolean t = false;
		boolean b = false;
		if (point.x - 1 >= 0) {
			IGameMapPiece p = getMapPiece(point.x - 1, point.y);
			if (ignoreHinder || !hitTest(p)) {
				oa.add(p);
				l = true;
			}
		}
		if (point.x + 1 < mx) {
			IGameMapPiece p = getMapPiece(point.x + 1, point.y);
			if (ignoreHinder || !hitTest(p)) {
				oa.add(p);
				r = true;
			}
		}
		if (point.y - 1 >= 0) {
			IGameMapPiece p = getMapPiece(point.x, point.y - 1);
			if (ignoreHinder || !hitTest(p)) {
				oa.add(p);
				b = true;
			}
		}
		if (point.y + 1 < my) {
			IGameMapPiece p = getMapPiece(point.x, point.y + 1);
			if (ignoreHinder || !hitTest(p)) {
				oa.add(p);
				t = true;
			}
		}
		if (allowInclined) {
			if (l && t) {
				IGameMapPiece p = getMapPiece(point.x - 1, point.y + 1);
				if (ignoreHinder || !hitTest(p))
					oa.add(p);
			}
			if (l && b) {
				IGameMapPiece p = getMapPiece(point.x - 1, point.y - 1);
				if (ignoreHinder || !hitTest(p))
					oa.add(p);
			}
			if (r && t) {
				IGameMapPiece p = getMapPiece(point.x + 1, point.y + 1);
				if (ignoreHinder || !hitTest(p))
					oa.add(p);
			}
			if (r && b) {
				IGameMapPiece p = getMapPiece(point.x + 1, point.y - 1);
				if (ignoreHinder || !hitTest(p))
					oa.add(p);
			}
		}

		return oa;
	}

	/**
	 * 计算路线
	 * 
	 * @param ignoreHinder
	 *            忽略障碍
	 * @return 路线列表
	 */
	public CopyOnWriteArrayList<IGameMapPiece> CalculatingRoute(boolean ignoreHinder) {
		// Date sd = new Date();

		IGameMapPiece start = getMapPiece(getStartingPoint());
		IGameMapPiece end = getMapPiece(getFinishingPoint());
		Hashtable<IGameMapPiece, IGameMapPiece> h = pathfinding(start, end,
				new Hashtable<IGameMapPiece, IGameMapPiece>(),
				new Hashtable<IGameMapPiece, IGameMapPiece>(), ignoreHinder);

		// System.out.println("寻路耗时"+(new Date().getTime()-sd.getTime())+"ms");

		if (h == null)
			return null;
		CopyOnWriteArrayList<IGameMapPiece> a = new CopyOnWriteArrayList<IGameMapPiece>();
		IGameMapPiece now = end;
		do {
			a.add(0, now);
			// System.out.println(now.getIndexLocation()+"  -  "+h.get(now).getIndexLocation());
			now = h.get(now);
		} while (!now.equals(start));
		// for (IGameMapPiece f : h.keySet()) {
		// a.add(f);
		// System.out.println(f.getIndexLocation()+"  -  "+h.get(f).getIndexLocation());
		// }

		return a;
	}

	/**
	 * 寻路
	 * 
	 * @param start
	 *            起始地图块
	 * @param finish
	 *            终点地图块
	 * @param openlist
	 *            开启列表
	 * @param closelist
	 *            关闭列表
	 * @param ignoreHinder
	 *            忽略障碍
	 * @return 寻路结果映射表
	 */
	protected Hashtable<IGameMapPiece, IGameMapPiece> pathfinding(
			IGameMapPiece start, IGameMapPiece finish,
			Hashtable<IGameMapPiece, IGameMapPiece> openlist,
			Hashtable<IGameMapPiece, IGameMapPiece> closelist,
			boolean ignoreHinder) {
		// for (IGameMapPiece f : closelist.keySet()) {
		// if(!closelist.get(f).equals(start)){
		// float g =
		// calculationsDistance(f.getIndexLocation(),start.getIndexLocation());
		// if(calculationsDistance(f.getIndexLocation(),closelist.get(f).getIndexLocation())>g){
		// System.out.print(closelist.get(f).getIndexLocation()+"("+calculationsDistance(f.getIndexLocation(),closelist.get(f).getIndexLocation())+")"+"替换为：");
		// closelist.put(f, start);
		// System.out.println(closelist.get(f).getIndexLocation()+"("+calculationsDistance(f.getIndexLocation(),closelist.get(f).getIndexLocation())+")");
		// }
		// }
		// }
		if (openlist.containsKey(start)) {
			closelist.put(start, openlist.get(start));
			openlist.remove(start);
		}
		for (IGameMapPiece f : CheckAround(start.getIndexLocation(), true,
				ignoreHinder)) {
			if (openlist.containsKey(f)) {
				if (!openlist.get(f).equals(start)) {
					float g = calculationsDistance(f.getIndexLocation(),
							start.getIndexLocation())
							+ calculationsDistance(openlist.get(f)
									.getIndexLocation(),
									start.getIndexLocation());
					if (calculationsDistance(f.getIndexLocation(), openlist
							.get(f).getIndexLocation()) > g) {
						System.out.print(openlist.get(f).getIndexLocation()
								+ "("
								+ calculationsDistance(f.getIndexLocation(),
										openlist.get(f).getIndexLocation())
								+ ")" + "替换为：");
						openlist.put(f, start);
						System.out.println(openlist.get(f).getIndexLocation()
								+ "("
								+ calculationsDistance(f.getIndexLocation(),
										openlist.get(f).getIndexLocation())
								+ ")");
					}
				}
			} else if (closelist.containsKey(f)) {
				if (!closelist.get(f).equals(start)) {
					float g = calculationsDistance(f.getIndexLocation(),
							start.getIndexLocation())
							+ calculationsDistance(closelist.get(f)
									.getIndexLocation(),
									start.getIndexLocation());
					if (calculationsDistance(f.getIndexLocation(), closelist
							.get(f).getIndexLocation()) > g) {
						System.out.print(closelist.get(f).getIndexLocation()
								+ "("
								+ calculationsDistance(f.getIndexLocation(),
										closelist.get(f).getIndexLocation())
								+ ")" + "替换为：");
						closelist.put(f, start);
						System.out.println(closelist.get(f).getIndexLocation()
								+ "("
								+ calculationsDistance(f.getIndexLocation(),
										closelist.get(f).getIndexLocation())
								+ ")");
					}
				}
			} else {
				// if(hitTest(f)){
				// closelist.put(f, start);
				// }
				// else{
				openlist.put(f, start);
				if (f.equals(finish)) {
					closelist.put(finish, start);
					return closelist;
				}
				// }
			}
		}
		float v = Float.MAX_VALUE;
		IGameMapPiece select = null;
		for (IGameMapPiece f : openlist.keySet()) {
			// if(!openlist.get(f).equals(start)){
			// float g =
			// calculationsDistance(f.getIndexLocation(),start.getIndexLocation());
			// if(calculationsDistance(f.getIndexLocation(),openlist.get(f).getIndexLocation())>g){
			// System.out.print(openlist.get(f).getIndexLocation()+"("+calculationsDistance(f.getIndexLocation(),openlist.get(f).getIndexLocation())+")"+"替换为：");
			// openlist.put(f, start);
			// System.out.println(openlist.get(f).getIndexLocation()+"("+calculationsDistance(f.getIndexLocation(),openlist.get(f).getIndexLocation())+")");
			// }
			// }
			float as = assess(f.getIndexLocation(), start.getIndexLocation(),
					finish.getIndexLocation());
			if (as < v) {
				v = as;
				select = f;
			}
		}
		if (select == null)
			return null;
		// if(!openlist.get(select).equals(start)){
		// float g =
		// calculationsDistance(select.getIndexLocation(),start.getIndexLocation());
		// if(calculationsDistance(select.getIndexLocation(),openlist.get(select).getIndexLocation())>g){
		// System.out.print(openlist.get(select).getIndexLocation()+"("+calculationsDistance(select.getIndexLocation(),openlist.get(select).getIndexLocation())+")"+"替换为：");
		// openlist.put(select, start);
		// System.out.println(openlist.get(select).getIndexLocation()+"("+calculationsDistance(select.getIndexLocation(),openlist.get(select).getIndexLocation())+")");
		// }
		// }
		return pathfinding(select, finish, openlist, closelist, ignoreHinder);
	}

	/**
	 * 评估目标位置的分值，分值越低表示越有价值
	 * 
	 * @param assessTarget
	 *            评估目标位置
	 * @param start
	 *            起步位置
	 * @param finish
	 *            终点位置
	 * @return 分值
	 */
	protected float assess(Point assessTarget, Point start, Point finish) {
		return calculationsDistance(start, assessTarget)
				+ calculationsDistance(assessTarget, finish);
	}

	/**
	 * 计算两个位置的距离
	 * 
	 * @param a
	 *            位置A
	 * @param b
	 *            位置B
	 * @return 距离
	 */
	protected float calculationsDistance(Point a, Point b) {
		return new Vector2DF(a.x, a.y).minus(new Vector2DF(b.x, b.y))
				.getLength();
	}

	/**
	 * 碰撞测试
	 * 
	 * @param p
	 *            地图块
	 * @return 是否发生碰撞
	 */
	protected boolean hitTest(IGameMapPiece p) {
		return !p.getIsAllowPass();
	}

	/**
	 * 获取地图块
	 * 
	 * @param point
	 *            位置
	 * @return 地图块
	 */
	private IGameMapPiece getMapPiece(Point point) {
		return getMapPiece(point.x, point.y);
	}

	/**
	 * 获取地图块
	 * 
	 * @param x
	 *            横向索引位置
	 * @param y
	 *            纵向索引位置
	 * @return 地图块
	 */
	private IGameMapPiece getMapPiece(int x, int y) {
		return getMapData()[x][y];
	}
}
